package com.example.form.service.impl;

import com.example.form.common.AppResult;
import com.example.form.common.ResultCode;
import com.example.form.dao.UserMapper;
import com.example.form.exception.ApplicationException;
import com.example.form.model.User;
import com.example.form.service.IUserService;
import com.example.form.utils.MD5Util;
import com.example.form.utils.StringUtil;
import com.example.form.utils.UUIDUtil;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;

import static jdk.nashorn.internal.runtime.regexp.joni.Config.log;


@Service //被spring管理
@Slf4j
public class UserServiceImpl implements IUserService {
    @Autowired
    private UserMapper userMapper;//属性注入得到数据库操作类
    @Override //实现接口中的抽象方法-->创建普通用户(从前端传递来用户参数,然后添加到数据库中)
    public void createNormaluser(User user) {
        //1,非空校验
        if (user==null || StringUtil.isEmpty(user.getUsername()) ||StringUtil.isEmpty(user.getPassword())||StringUtil.isEmpty(user.getSalt())){
            //异常,打印日志,要加@Slf4j注解使用
            log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());//参数校验失败状态码
            //抛出异常,统一抛出ApplicationException自定义异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));//抛出参数校验错误异常
        }
        //2,按照用户名来查询用户信息
        User existsUser= userMapper.selectByUserName(user.getUsername());
        //2.1判断查询到的用户是否存在
        if(existsUser!=null){//代表当前的用户名,已经被别人注册了(已经有了)
            //打印日志
            log.info(ResultCode.AILED_USER_EXISTS.toString());//用户已经存在的状态码信息
            //抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.AILED_USER_EXISTS));//抛出自定义的用户已经存在的异常信息
        }
        //3,新增用户流程,
        //3.1设置用户信息的默认值除了注册时传⼊的有效值，其他全部填充默认值
        if (user.getGender() == null) {
            // 性别赋默认值
            user.setGender((byte) 2);
        }
        user.setIsAdmin((byte) 0);
        user.setAvatarUrl(null);
        user.setArticleCount(0);
        user.setState((byte) 0);
        user.setDeleteState((byte) 0);
        Date date = new Date();//当前日期
        user.setCreateTime(date);
        user.setUpdateTime(date);
        //3.2写入数据库
        int row = userMapper.insertSelective(user);//执行动态插入的SQL方法
        if(row!=1){//返回值为1才是插入成功
            //打印日志
            log.info(ResultCode.FAILED_CREATE.toString());//新增失败的状态信息
            throw new ApplicationException(ResultCode.FAILED_CREATE.toString());//抛出异常
        }
        //4,用户增加成功,打印日志
        log.info("用户增加成功:"+user.getUsername()+".");
    }

    @Override
    public User selectByUserName(String username) {
        //1非空校验
        if(StringUtil.isEmpty(username)){//参数异常
            //打印日志
            log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());//参数校验失败的状态码信息
            //抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));//抛出自定义的参数校验异常
        }
        //2返回查询到的结果
        return userMapper.selectByUserName(username);
    }

    @Override
    public User login(String username, String password) {
        //1,非空校验
        if (StringUtil.isEmpty(username)||StringUtil.isEmpty(password)){
            //打印日志
            log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());//参数校验失败的状态码信息
            //抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));//抛出自定义的参数校验异常
        }
        //2,按照用户名查询用户信息
        User user=userMapper.selectByUserName(username);//我们刚刚上面实现的接口中的按照用户名查询用户信息的方法
        //3,对查询结果进行非空校验
        if (user==null){//代表没有查询到
            //打印日志
            log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());//参数校验失败的状态码信息
            //抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));//抛出自定义的参数校验异常
        }
        //4对密码进行校验
        String encryptPassword= MD5Util.md5Salt(password,user.getSalt());//按照用户输入的密码,和查询数据库得到的盐值,得到加密的密码
        //5,对比现在加密的密码和数据库中的是否相等
        if (!encryptPassword.equals(user.getPassword())){//密码不对
            //打印日志
            log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());//参数校验失败的状态码信息
            //抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));//抛出自定义的参数校验异常
        }
        //6,走到这里,代表用户登录成功,打印日志
        log.info("登录成功.name:"+username);
        //7,返回登录的用户的信息
        return user;
    }

    @Override
    public User selectById(Long id) {
        //1,非空校验
        if(id==null){
            //打印日志
            log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
            //抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_LOGIN.toString()));//抛出用户名或密码错误的自定义异常
        }
        //调用dao查询数据库
        return userMapper.selectByPrimaryKey(id);
    }

    @Override
    public void addOneArticleCountById(Long id) {
        //非空判断
        if (id==null||id<0){//代表传入的用户id有问题
            //打印日志
            log.warn(ResultCode.FAILED_BOARD_ARTICLE_COUNT.toString());//打印更新帖子数量失败
            //抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_BOARD_ARTICLE_COUNT));
        }
        //根据传入的用户id,获取对应的用户对象
        User user=userMapper.selectByPrimaryKey(id);
        //检验得到的用户对象
        if (user==null){
            // 打印⽇志
            log.warn(ResultCode.ERROR_IS_NULL.toString() + ", user id = " + id);
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.ERROR_IS_NULL));
        }
        //更新用户的发帖数信息
        User updateUser=new User();
        updateUser.setId(user.getId());
        updateUser.setArticleCount(user.getArticleCount()+1);//原有的数量加一
        int row=userMapper.updateByPrimaryKeySelective(updateUser);//执行修改的SQL
        //判断受影响的行数
        if (row != 1) {
            log.warn(ResultCode.FAILED.toString() + ", 受影响的⾏数不等于 1 .");
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED));
        }
    }

    @Override
    public void subOneArticleCountById(Long id) {
        //非空判断
        if (id==null||id<0){//代表传入的用户id有问题
            //打印日志
            log.warn(ResultCode.FAILED_BOARD_ARTICLE_COUNT.toString());//打印更新帖子数量失败
            //抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_BOARD_ARTICLE_COUNT));
        }
        //根据传入的用户id,获取对应的用户对象
        User user=userMapper.selectByPrimaryKey(id);
        //检验得到的用户对象
        if (user==null){
            // 打印⽇志
            log.warn(ResultCode.ERROR_IS_NULL.toString() + ", user id = " + id);
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.ERROR_IS_NULL));
        }
        //更新用户的发帖数信息
        User updateUser=new User();
        updateUser.setId(user.getId());
        updateUser.setArticleCount(user.getArticleCount()-1);//原有的数量减一
        int row=userMapper.updateByPrimaryKeySelective(updateUser);//执行修改的SQL
        //判断受影响的行数
        if (row != 1) {
            log.warn(ResultCode.FAILED.toString() + ", 受影响的⾏数不等于 1 .");
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED));
        }
    }

    @Override
    public void modifyInfo(User user) {
        //1,非空判断
        if (user==null||user.getId()==null||user.getId()<0){//代表传入的用户id有问题
            //打印日志
            log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());//参数校验异常
            //抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
        }
        //2,校验用户是否存在(也是获取用户信息)
        User existsUser=userMapper.selectByPrimaryKey(user.getId());//因为id已经校验了,是存在的,所以可以使用
        if(existsUser==null){//代表当前id对应的用户不存在
            //打印日志
            log.warn(ResultCode.FAILED_USER_NOT_EXISTS.toString());//用户不存在异常
            //抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_USER_NOT_EXISTS));
        }
        //3,定义一个标志位
        boolean checkAttr=false;//标志位
        //4,定义一个专门用来更新的对应,防止用户传入的user对象设置了其他属性
        User updateUser=new User();
        //当使用动态SQL的时候,覆盖了没有经过校验的字段

        //5,设置用户id
        updateUser.setId(user.getId());
        //6,对每一个需要修改的参数进行校验,判断是否存在
        //用户名(登录名)(具有唯一性)
        // 先校验传入的用户是否有用户名参数,然后再与数据库中通过传入的用户id查询到的用户信息的用户名来进行比较,若不相等,则代表前端传入了新的用户名,所以要修改用户名
        if (!StringUtil.isEmpty(user.getUsername())//因为existsUser.getUsername()没有判断过是否为空,所以不调用equals方法,使用user.getUsername()调用,因为判断了isEmpty
            && !user.getUsername().equals(existsUser.getUsername())){
            //需要修改用户名
            //用户名唯一性校验
            User checkUser=userMapper.selectByUserName(user.getUsername());//根据前端传入的用户名来查询对应的用户信息
            if (checkUser!=null){//代表当前前端传入的用户名在数据库中已经存在,所以不能使用这个用户名(登录名) 用户名已经存在
                //打印日志
                log.warn(ResultCode.AILED_USER_EXISTS.toString());//用户已经存在异常
                //抛出异常
                throw new ApplicationException(AppResult.failed(ResultCode.AILED_USER_EXISTS));
            }
            //数据库中没有前端传入的用户名,则表示可以修改用户名
            //设置要修改的用户名
            updateUser.setUsername(user.getUsername());
            //更新标志位
            checkAttr=true;//代表可以执行动态SQL了,因为已经有一项属性需要修改了(用户名需要)
        }
        //7,校验昵称
        if(!StringUtil.isEmpty(user.getNickname())
           && !user.getNickname().equals(existsUser.getNickname())){//传入的昵称和数据库中通过用户id查询到的昵称不一致,代表传入了新的昵称,所以要修改昵称
            //设置要修改的昵称
            updateUser.setNickname(user.getNickname());
            //设置标志位
            checkAttr=true;
        }
        //8,校验性别
        if(user.getGender() != null && user.getGender() !=
                existsUser.getGender()){//传入的性别和数据库中通过用户id查询到的性别不一致,代表传入了新的性别,所以要修改性别
            //设置要修改的性别
            updateUser.setGender(user.getGender());
            //设置标志位
            checkAttr=true;
        }
        // 9. 校验邮箱
        if (!StringUtil.isEmpty(user.getEmail())
                && !user.getEmail().equals(existsUser.getEmail())) {
            // 设置邮箱
            updateUser.setEmail(user.getEmail());
            // 更新标志位
            checkAttr = true;
        }
        // 10. 校验电话号码
        if (!StringUtil.isEmpty(user.getPhoneNum())
                && !user.getPhoneNum().equals(existsUser.getPhoneNum())) {
            // 设置电话号码
            updateUser.setPhoneNum(user.getPhoneNum());
            // 更新标志位
            checkAttr = true;
        }
        //11,校验个人简介
        if(!StringUtil.isEmpty(user.getRemark())
                && !user.getRemark().equals(existsUser.getRemark())){//传入的电话号码和数据库中通过用户id查询到的电话号码不一致,代表传入了新的电话号码,所以要修改电话号码
            //设置要修改的个人简介
            updateUser.setRemark(user.getRemark());
            //设置标志位
            checkAttr=true;
        }
        //11,根据标志位来决定是否需要执行动态修改
        if(checkAttr==false){//标志位为FALSE,则代表上面要修改的参数一个都没有,所以不执行SQL
            //打印日志
            log.warn("没有要修改的参数");//操作失败异常
            //抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED));
        }
        //走到这里,代表传入的参数中有要修改的属性
        //12,调用dao执行动态SQL
        int row=userMapper.updateByPrimaryKeySelective(user);//根据用户id找到要修改的用户,然后修改属性
        //判断执行的结果
        if (row!=1){
            //打印日志
            log.warn(ResultCode.FAILED.toString());//操作失败
            //抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED));
        }
    }

    @Override
    public void modifyPassword(Long id, String newPassword, String oldPassword) {
        //参数校验
        if(id==null||id<=0||StringUtil.isEmpty(newPassword)||StringUtil.isEmpty(oldPassword)){
            //打印日志
            log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());//参数校验失败异常
            //抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
        }
        //根据id查询用户信息
        User user=userMapper.selectByPrimaryKey(id);
        //校验用户是否存在
        if (user==null){
            //打印日志
            log.warn(ResultCode.FAILED_USER_NOT_EXISTS.toString());//用户不存在异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_USER_NOT_EXISTS));
        }
        //判断用户传入的原密码是否正确
        //先使用原密码与获取到的用户的盐值进行MD5加密
        String oldEncryptPassword=MD5Util.md5Salt(oldPassword,user.getSalt());//把传入的原密码和根据id查询到的用户的盐值进行加密得到密文
        //然后把密文和根据id查询到的用户的原密码的密文进行比较
        if(!oldEncryptPassword.equalsIgnoreCase(user.getPassword())){//忽略大小写的equals比较
            //打印日志
            log.warn(ResultCode.FAILED_TWO_PWD_NOT_SAME.toString());//两次输入的密码不一致异常
            //抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_TWO_PWD_NOT_SAME));
        }
        //生成一个新的盐值(因为要更改新的密码,所以把盐值也更新一下比较好)
        String salt= UUIDUtil.UUID_32();//得到32为的盐值
        //生成新密码的密文(和新的盐值进行MD5加密
        String encryptPassword=MD5Util.md5Salt(newPassword,salt);//新密码与新盐值
        //构造要更新的对象
        User updateUser=new User();
        updateUser.setId(id);//设置id
        updateUser.setSalt(salt);//设置盐值
        updateUser.setPassword(encryptPassword);//设置新密码对应的密文
        Date date=new Date();
        updateUser.setUpdateTime(date);//设置修改时间
        //调用dao
        int row=userMapper.updateByPrimaryKeySelective(updateUser);
        //判断执行的结果
        if(row!=1){
            //打印日志
            log.warn(ResultCode.FAILED.toString());//操作失败
            //抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED));
        }
    }
}